package com.xyzboom.compiler.cli

import com.fasterxml.jackson.core.type.TypeReference
import com.fasterxml.jackson.module.kotlin.jsonMapper
import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.parameters.options.flag
import com.github.ajalt.clikt.parameters.options.option
import com.github.ajalt.clikt.parameters.options.required
import com.github.ajalt.clikt.parameters.types.file
import com.xyzboom.compiler.compiler.lexer.LexerRule
import com.xyzboom.compiler.compiler.lexer.parseCode
import com.xyzboom.compiler.compiler.lexer.parseLexer
import mu.KotlinLogging
import java.io.File

private val logger = KotlinLogging.logger {}

/**
 * Lexer command
 *
 * @constructor Create empty Lexer command
 */
class LexerCommand : CliktCommand() {
    private val lexerFile by option(
        "-i", "--input",
        help = "lexer file input"
    ).file()
        .required()
    private val isRaw by option("-r", "--raw", help = "input lexer file is raw or json decoded")
        .flag("-d", "--decoded", default = true)
    private val codeFile by option(
        "-ic", "--input-code",
        help = "code file input"
    ).file()

    private val lexerOutFile by option(
        "-o", "--output",
        help = "decoded lexer file output path"
    ).file()
    private val tokenOutFile by option(
        "-ot", "--output-token",
        help = "decoded token file output path"
    )

    /**
     * Run LexerCommand
     */
    override fun run() {
        val jsonMapper = jsonMapper()
        val lexerRules = if (isRaw) {
            parseLexer(lexerFile.readText())
        } else {
            jsonMapper.readerFor(object : TypeReference<List<LexerRule>>() {})
                .readValue(lexerFile.readText())
        }
        if (!isRaw && lexerOutFile != null) {
            echo("warning: your input is decoded lexer, so there will be no output")
        } else if (lexerOutFile != null) {
            lexerOutFile!!.writeText(
                jsonMapper.writeValueAsString(lexerRules)
            )
        }
        logger.trace { "lexer finished" }
        if (codeFile == null) {
            logger.trace { "no code input, exiting" }
            return
        }
        val tokens = parseCode(codeFile!!.readText(), lexerRules)
        for (token in tokens) {
            logger.trace { "(${token?.lineIndex}:${token?.indexInLine}, ${token?.typeName}, ${token?.info})" }
        }
        if (tokenOutFile != null) {
            File(tokenOutFile!!).writeText(
                jsonMapper.writeValueAsString(ArrayList(tokens).apply { add(null) })
            )
        }
    }

}